#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
服务依赖分析器 - Service Dependency Analyzer
分析各个服务的导入依赖关系，生成迁移优先级
"""

import os
import sys
import ast
import json
import logging
from pathlib import Path
from typing import Dict, List, Set, Optional, Tuple
from dataclasses import dataclass, asdict
from collections import defaultdict, deque
import networkx as nx
import matplotlib.pyplot as plt
from datetime import datetime


@dataclass
class ImportInfo:
    """导入信息"""
    module: str
    alias: Optional[str] = None
    from_import: bool = False
    items: List[str] = None
    line_number: int = 0
    
    def __post_init__(self):
        if self.items is None:
            self.items = []


@dataclass 
class ServiceInfo:
    """服务信息"""
    name: str
    path: str
    imports: List[ImportInfo]
    internal_deps: Set[str]  # 内部依赖
    external_deps: Set[str]  # 外部依赖
    complexity_score: float = 0.0
    migration_priority: int = 0
    
    def __post_init__(self):
        if isinstance(self.internal_deps, list):
            self.internal_deps = set(self.internal_deps)
        if isinstance(self.external_deps, list):
            self.external_deps = set(self.external_deps)


class DependencyAnalyzer:
    """依赖分析器"""
    
    def __init__(self, project_root: str = None):
        self.project_root = Path(project_root) if project_root else Path.cwd()
        self.logger = self._setup_logger()
        
        # 分析结果
        self.services: Dict[str, ServiceInfo] = {}
        self.dependency_graph: nx.DiGraph = nx.DiGraph()
        self.conflict_groups: List[List[str]] = []
        self.migration_order: List[str] = []
        
        # 配置
        self.service_directories = [
            "backend/services",
            "backend/shared", 
            "shared"
        ]
        
        self.ignore_patterns = [
            "__pycache__",
            "*.pyc",
            "*.pyo",
            ".pytest_cache",
            "node_modules",
            ".git"
        ]
    
    def _setup_logger(self) -> logging.Logger:
        """设置日志"""
        logger = logging.getLogger('dependency_analyzer')
        if not logger.handlers:
            handler = logging.StreamHandler()
            formatter = logging.Formatter(
                '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
            )
            handler.setFormatter(formatter)
            logger.addHandler(handler)
            logger.setLevel(logging.INFO)
        return logger
    
    def analyze_project(self) -> Dict[str, any]:
        """分析整个项目"""
        self.logger.info("开始项目依赖分析...")
        
        # 1. 扫描所有服务
        self._scan_services()
        
        # 2. 分析依赖关系
        self._analyze_dependencies()
        
        # 3. 构建依赖图
        self._build_dependency_graph()
        
        # 4. 检测冲突
        self._detect_conflicts()
        
        # 5. 计算迁移优先级
        self._calculate_migration_priority()
        
        # 6. 生成迁移顺序
        self._generate_migration_order()
        
        self.logger.info("项目依赖分析完成!")
        
        return self._generate_analysis_report()
    
    def _scan_services(self):
        """扫描所有服务"""
        self.logger.info("扫描服务目录...")
        
        for service_dir in self.service_directories:
            dir_path = self.project_root / service_dir
            if not dir_path.exists():
                continue
                
            self.logger.info(f"扫描目录: {dir_path}")
            self._scan_directory(dir_path, service_dir)
    
    def _scan_directory(self, dir_path: Path, relative_path: str):
        """扫描目录中的Python文件"""
        for item in dir_path.iterdir():
            if any(pattern in str(item) for pattern in self.ignore_patterns):
                continue
                
            if item.is_file() and item.suffix == '.py':
                self._analyze_python_file(item, relative_path)
            elif item.is_dir():
                self._scan_directory(item, relative_path)
    
    def _analyze_python_file(self, file_path: Path, service_dir: str):
        """分析Python文件的导入"""
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()
            
            tree = ast.parse(content)
            imports = []
            
            for node in ast.walk(tree):
                if isinstance(node, ast.Import):
                    for alias in node.names:
                        imports.append(ImportInfo(
                            module=alias.name,
                            alias=alias.asname,
                            line_number=node.lineno
                        ))
                
                elif isinstance(node, ast.ImportFrom):
                    if node.module:
                        items = [alias.name for alias in node.names]
                        imports.append(ImportInfo(
                            module=node.module,
                            from_import=True,
                            items=items,
                            line_number=node.lineno
                        ))
            
            # 确定服务名称
            relative_file = file_path.relative_to(self.project_root)
            service_name = self._extract_service_name(relative_file, service_dir)
            
            if service_name not in self.services:
                self.services[service_name] = ServiceInfo(
                    name=service_name,
                    path=str(relative_file),
                    imports=[],
                    internal_deps=set(),
                    external_deps=set()
                )
            
            self.services[service_name].imports.extend(imports)
            
        except Exception as e:
            self.logger.warning(f"分析文件失败 {file_path}: {e}")
    
    def _extract_service_name(self, file_path: Path, service_dir: str) -> str:
        """提取服务名称"""
        parts = file_path.parts
        
        # 对于backend/services/*的情况
        if 'services' in parts:
            services_idx = parts.index('services')
            if services_idx + 1 < len(parts):
                return parts[services_idx + 1]
        
        # 对于backend/shared/*的情况
        if 'shared' in parts:
            shared_idx = parts.index('shared')
            if shared_idx + 1 < len(parts):
                return f"shared.{parts[shared_idx + 1]}"
        
        # 默认使用第一个目录名
        if len(parts) > 1:
            return parts[1] if parts[0] in ['backend', 'shared'] else parts[0]
        
        return 'unknown'
    
    def _analyze_dependencies(self):
        """分析依赖关系"""
        self.logger.info("分析服务依赖关系...")
        
        for service_name, service_info in self.services.items():
            for import_info in service_info.imports:
                module = import_info.module
                
                # 判断是内部依赖还是外部依赖
                if self._is_internal_dependency(module):
                    target_service = self._resolve_service_from_module(module)
                    if target_service and target_service != service_name:
                        service_info.internal_deps.add(target_service)
                else:
                    service_info.external_deps.add(module)
    
    def _is_internal_dependency(self, module: str) -> bool:
        """判断是否为内部依赖"""
        internal_prefixes = [
            'backend.',
            'shared.',
            'config.',
            'adapters.',
            'scripts.'
        ]
        
        return any(module.startswith(prefix) for prefix in internal_prefixes)
    
    def _resolve_service_from_module(self, module: str) -> Optional[str]:
        """从模块路径解析服务名"""
        parts = module.split('.')
        
        # backend.services.xxx -> xxx
        if len(parts) >= 3 and parts[0] == 'backend' and parts[1] == 'services':
            return parts[2]
        
        # backend.shared.xxx -> shared.xxx
        if len(parts) >= 3 and parts[0] == 'backend' and parts[1] == 'shared':
            return f"shared.{parts[2]}"
        
        # shared.xxx -> shared.xxx
        if len(parts) >= 2 and parts[0] == 'shared':
            return f"shared.{parts[1]}"
        
        return None
    
    def _build_dependency_graph(self):
        """构建依赖图"""
        self.logger.info("构建依赖图...")
        
        # 添加节点
        for service_name in self.services:
            self.dependency_graph.add_node(service_name)
        
        # 添加边
        for service_name, service_info in self.services.items():
            for dep in service_info.internal_deps:
                if dep in self.services:
                    self.dependency_graph.add_edge(service_name, dep)
    
    def _detect_conflicts(self):
        """检测循环依赖和冲突"""
        self.logger.info("检测依赖冲突...")
        
        # 检测强连通分量（循环依赖）
        strongly_connected = list(nx.strongly_connected_components(self.dependency_graph))
        
        for component in strongly_connected:
            if len(component) > 1:
                self.conflict_groups.append(list(component))
                self.logger.warning(f"发现循环依赖: {component}")
    
    def _calculate_migration_priority(self):
        """计算迁移优先级"""
        self.logger.info("计算迁移优先级...")
        
        for service_name, service_info in self.services.items():
            # 基础复杂度得分
            import_count = len(service_info.imports)
            internal_dep_count = len(service_info.internal_deps)
            external_dep_count = len(service_info.external_deps)
            
            # 计算复杂度得分
            complexity_score = (
                import_count * 0.3 +
                internal_dep_count * 0.5 +
                external_dep_count * 0.2
            )
            
            # 依赖深度影响
            try:
                in_degree = self.dependency_graph.in_degree(service_name)
                out_degree = self.dependency_graph.out_degree(service_name)
                
                # 被依赖越多，优先级越高（先修复）
                # 依赖别人越多，优先级越低（后修复）
                priority_score = in_degree * 2 - out_degree
                
            except:
                priority_score = 0
            
            service_info.complexity_score = complexity_score
            service_info.migration_priority = int(priority_score)
    
    def _generate_migration_order(self):
        """生成迁移顺序"""
        self.logger.info("生成迁移顺序...")
        
        try:
            # 检查是否是有向无环图
            if nx.is_directed_acyclic_graph(self.dependency_graph):
                # 尝试拓扑排序
                topological_order = list(nx.topological_sort(self.dependency_graph))
                
                # 按优先级调整顺序
                priority_services = [(name, self.services[name].migration_priority) 
                                   for name in topological_order]
                priority_services.sort(key=lambda x: x[1], reverse=True)
                
                self.migration_order = [name for name, _ in priority_services]
            else:
                raise nx.NetworkXError("Graph contains cycles")
                
        except nx.NetworkXError:
            # 如果有循环依赖，使用其他策略
            self.logger.warning("存在循环依赖，使用替代排序策略")
            
            # 按优先级和复杂度排序
            services_by_priority = [
                (name, info.migration_priority, info.complexity_score)
                for name, info in self.services.items()
            ]
            services_by_priority.sort(key=lambda x: (x[1], -x[2]), reverse=True)
            
            self.migration_order = [name for name, _, _ in services_by_priority]
    
    def _generate_analysis_report(self) -> Dict[str, any]:
        """生成分析报告"""
        return {
            'timestamp': datetime.now().isoformat(),
            'services': {
                name: {
                    'path': info.path,
                    'import_count': len(info.imports),
                    'internal_deps': list(info.internal_deps),
                    'external_deps': list(info.external_deps),
                    'complexity_score': info.complexity_score,
                    'migration_priority': info.migration_priority
                }
                for name, info in self.services.items()
            },
            'dependency_stats': {
                'total_services': len(self.services),
                'total_dependencies': self.dependency_graph.number_of_edges(),
                'circular_dependencies': len(self.conflict_groups),
                'conflict_groups': self.conflict_groups
            },
            'migration_plan': {
                'recommended_order': self.migration_order,
                'high_priority_services': [
                    name for name in self.migration_order[:5]
                ],
                'conflict_resolution_needed': len(self.conflict_groups) > 0
            }
        }
    
    def export_report(self, output_file: str = None):
        """导出分析报告"""
        if not output_file:
            output_file = self.project_root / "logs" / "migration" / f"dependency_analysis_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
        
        # 确保目录存在
        output_path = Path(output_file)
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        report = self._generate_analysis_report()
        
        with open(output_path, 'w', encoding='utf-8') as f:
            json.dump(report, f, indent=2, ensure_ascii=False)
        
        self.logger.info(f"分析报告已导出到: {output_path}")
        return output_path
    
    def visualize_dependencies(self, output_file: str = None):
        """可视化依赖关系图"""
        if not output_file:
            output_file = self.project_root / "logs" / "migration" / f"dependency_graph_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
        
        try:
            plt.figure(figsize=(15, 10))
            
            # 使用spring布局
            pos = nx.spring_layout(self.dependency_graph, k=3, iterations=50)
            
            # 绘制节点
            node_colors = []
            for node in self.dependency_graph.nodes():
                if node in [group for group in self.conflict_groups for item in group]:
                    node_colors.append('red')  # 冲突节点
                else:
                    priority = self.services[node].migration_priority
                    if priority > 2:
                        node_colors.append('lightgreen')  # 高优先级
                    elif priority > 0:
                        node_colors.append('yellow')  # 中优先级
                    else:
                        node_colors.append('lightblue')  # 低优先级
            
            nx.draw_networkx_nodes(
                self.dependency_graph, pos, 
                node_color=node_colors,
                node_size=1000,
                alpha=0.8
            )
            
            # 绘制边
            nx.draw_networkx_edges(
                self.dependency_graph, pos,
                edge_color='gray',
                arrows=True,
                arrowsize=20,
                alpha=0.6
            )
            
            # 绘制标签
            nx.draw_networkx_labels(
                self.dependency_graph, pos,
                font_size=8,
                font_weight='bold'
            )
            
            plt.title('服务依赖关系图\n(红色=冲突, 绿色=高优先级, 黄色=中优先级, 蓝色=低优先级)', 
                     fontsize=14, pad=20)
            plt.axis('off')
            plt.tight_layout()
            
            # 确保目录存在
            output_path = Path(output_file)
            output_path.parent.mkdir(parents=True, exist_ok=True)
            
            plt.savefig(output_path, dpi=300, bbox_inches='tight')
            plt.close()
            
            self.logger.info(f"依赖关系图已保存到: {output_path}")
            
        except ImportError:
            self.logger.warning("无法导入matplotlib，跳过图形生成")
        except Exception as e:
            self.logger.error(f"生成依赖关系图失败: {e}")


if __name__ == "__main__":
    # 运行依赖分析
    analyzer = DependencyAnalyzer()
    
    print("=== RedFire 服务依赖分析 ===")
    
    # 执行分析
    report = analyzer.analyze_project()
    
    # 输出简要报告
    print(f"\n总计发现服务: {report['dependency_stats']['total_services']}")
    print(f"总计依赖关系: {report['dependency_stats']['total_dependencies']}")
    print(f"循环依赖组: {report['dependency_stats']['circular_dependencies']}")
    
    print(f"\n推荐迁移顺序:")
    for i, service in enumerate(report['migration_plan']['recommended_order'][:10], 1):
        priority = analyzer.services[service].migration_priority
        complexity = analyzer.services[service].complexity_score
        print(f"{i:2d}. {service:<20} (优先级: {priority:2d}, 复杂度: {complexity:.1f})")
    
    if report['dependency_stats']['circular_dependencies'] > 0:
        print(f"\n⚠️  发现循环依赖组:")
        for i, group in enumerate(report['dependency_stats']['conflict_groups'], 1):
            print(f"{i}. {' -> '.join(group)}")
    
    # 导出详细报告
    report_file = analyzer.export_report()
    print(f"\n详细报告已导出到: {report_file}")
    
    # 生成可视化图
    analyzer.visualize_dependencies()
